///#######################################################################
///#      plane3d.lua - class that implements a 3d Plane.
///#######################################################################
///#      Autor - AMiranda, June/2009 (get from cgcPlane)
///#######################################################################

#include "plane3d.h"

//
//////////////////////////////////////////////////////////////////////////
//Plane3D::Plane3D(void)
//{
//}

//
//////////////////////////////////////////////////////////////////////////
Plane3D::Plane3D( const Point3D& pt0, const Point3D &pt1, const Point3D& pt2 )
{
  this->m_origin =  pt0;
  this->m_u      =  pt1 - pt0;
  this->m_u.normalize ( this->m_u );
  this->m_v      =  pt2 - pt0;
  this->m_v.normalize ( this->m_v );
  this->m_normal =  this->m_u.crossProd (this->m_v); // u v n is a right handed coordinate system. 
  this->m_v      =  this->m_normal.crossProd (this->m_u);  // to make sure that v is perpendicular to u.
  this->d        = -this->m_normal.dotProd ( pt0 );
}

//
//////////////////////////////////////////////////////////////////////////
Plane3D::Plane3D( const Point3D& origin, const Point3D &normal )
{
  this->rebind ( origin, normal );
}

//
//////////////////////////////////////////////////////////////////////////
Plane3D::~Plane3D(void)
{
}

/**  reinitializes a plane. 
@param origin plane origin.
@param normal plane normal.
*///////////////////////////////////////////////////////////////////////////
void 
Plane3D::rebind( const Point3D& _origin, const Point3D& _normal )
{
  this->m_origin = _origin;
  this->m_normal = _normal;
  this->d      = -_normal.dotProd ( _origin );
#if 1
  if ( !geomLibCompare( _normal.getZ(), 0.0, geomLibTOLERANCE ) )
  {
    this->m_u.setXYZ ( 0.0, 0.0, 1.0 );
  }
  else
  {
    this->m_u.setXYZ ( 0.0, 1.0, -_normal.getY()/_normal.getZ() );
    this->m_u.normalize ( this->m_u );
  } 
  this->m_v = _normal.crossProd (this->m_u);
#endif
}

//
//////////////////////////////////////////////////////////////////////////
void 
Plane3D::setOrigin( const Point3D& _o )
{
  this->m_origin = _o; 
  this->d = -this->m_normal.dotProd ( _o );
}

//
//////////////////////////////////////////////////////////////////////////
void 
Plane3D::setNormal( const Point3D& _n )
{
  this->rebind (this->m_origin, _n);
}

//
//////////////////////////////////////////////////////////////////////////
Point3D 
Plane3D::getOrigin() const
{
  return this->m_origin;
}

//
//////////////////////////////////////////////////////////////////////////
Point3D 
Plane3D::getNormal() const
{
  return this->m_normal;
}

//
//////////////////////////////////////////////////////////////////////////
void 
Plane3D::getLocalCoordinateSystem(Point3D& _u, 
                                  Point3D& _v, 
                                  Point3D& _n, 
                                  Point3D& _o ) const
{
  _u = this->m_u;
  _v = this->m_v;
  _n = this->m_normal;
  _o = this->m_origin;
}

//
//////////////////////////////////////////////////////////////////////////
Plane3D& 
Plane3D::operator=( const Plane3D& _p )
{
  this->m_u      = _p.m_u; 
  this->m_v      = _p.m_v; 
  this->m_origin = _p.m_origin; 
  this->m_normal = _p.m_normal;
  this->d      = _p.d;
  return *this;
}

//
//////////////////////////////////////////////////////////////////////////
Plane3D 
Plane3D::operator-() const
{
  return Plane3D ( this->m_origin, -this->m_normal );
}

//
//////////////////////////////////////////////////////////////////////////
void Plane3D::print( ostream& _os, const char* _sthing ) const
{
  _os << _sthing << "Origin: " << this->m_origin;
  _os << _sthing << "Normal: " << this->m_normal;
  _os << _sthing << "u vect: " << this->m_u;
  _os << _sthing << "v vect: " << this->m_v;
  _os << _sthing << "d     : " << this->d << "\n";
}

//
//////////////////////////////////////////////////////////////////////////
Point3D 
Plane3D::clstPoint( const Point3D& _pt ) const
{
  Point3D clst;

  clstPoint ( _pt, clst );

  return clst;
}

/**
generates the  Cartesian point corresponding to the evaluation 
of two parametric values (u and v) of a plane given in a parametric form.

@param u first parametric value.
@param v second parametric value.
@param pt  evaluated  Cartesian point.
*/
//////////////////////////////////////////////////////////////////////////
void   
Plane3D::eval (double _u,      /* parametric surface values.    (IN)  */
               double _v,
               Point3D&   _pt  ) const    /* evaluated  Cartesian point.    (OUT) */
{
  _pt = this->m_origin + (this->m_u * _u) + (this->m_v * _v);
}

//
//////////////////////////////////////////////////////////////////////////
Point3D 
Plane3D::eval( double _u, double _v ) const
{
  Point3D p; 

  this->eval ( _u, _v, p ); 
  return p;
}

/**
computes the  Cartesian coordinates of the closest
point on a plane, to a given point. It is used the plane 
implicit form.

@param pt given point.
@param clst closest point on the plane.
*/
//////////////////////////////////////////////////////////////////////////
void   
Plane3D::clstPoint (const Point3D&  _pt,      /*  Cartesian point.   (IN)  */
                    Point3D&  _clst ) const    /* closest point.   (OUT) */
{
  double dist = this->distanceToPoint ( _pt );

  _clst = _pt - dist * this->m_normal; 
}


/**
computes the  Cartesian coordinates of the closest point on a plane,
to a given  point.
It is used the plane normalized parametric form.

@param pt given point.
@param clst closest point.
*/
//////////////////////////////////////////////////////////////////////////
void 
Plane3D::clstPointNorm ( const Point3D&  _pt,       /*  Cartesian point.    (IN)  */
                         Point3D&  _clst ) const     /* closest point.      (OUT) */
{
  Point3D local_pt = ( _pt - this->m_origin );
  /*
  It is assumed that the two base vectors on the plane,
  "u" and "v", are perpendicular-to-each-other unity vectors.
  */
  this->eval ( local_pt.dotProd( this->m_u ), local_pt.dotProd( this->m_v ), _clst );
}


/**
returns the signed distance between a point and a
plane. The sign is positive if the point is in the half space
pointed to by the plane normal, and negative otherwise. <br>

|-> * ( + ) and <-| * ( - )

@param pt given point.
@return the signed distance between a point and a plane.
*/
//////////////////////////////////////////////////////////////////////////
double   
Plane3D::distanceToPoint (const Point3D&  _pt ) const  /* given point  (IN) */
{
  return ( _pt.dotProd ( this->m_normal ) + this->d );
}
